package com.ruoyi.system.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMwebOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.system.constants.WXPayConstants;
import com.ruoyi.system.service.WeChatPayService;
import com.ruoyi.system.service.WxNewPayService;
import com.ruoyi.system.tools.WxNewPayConfiguration;
import com.ruoyi.system.utils.HttpUtils;
import com.ruoyi.system.utils.WXPayUtil;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 微信支付
 */
@Service
@Slf4j
public class WeChatPayServiceImpl implements WeChatPayService {

    private static Config config = null;

    @Value("${wx.pay.path}")
    private String path;
    @Value("${wx.pay.appid}")
    private String APP_ID;
    @Value("${wx.pay.mer_id}")
    private String MCH_ID;
    @Value("${wx.pay.privateKey}")
    private String privateKey;
    @Value("${wx.pay.key}")
    private String KEY;
    @Value("${wx.pay.appsecret}")
    private String appsecret;
    @Value("${wx.pay.merchantSerialNumber}")
    private String merchantSerialNumber;
    //引入配置文件的值
    public WxNewPayService getNewWxPayService() {
        WxPayConfig payConfig = new WxPayConfig();
        //支付公众号appid
        payConfig.setAppId(APP_ID);
        //支付商户号mchID
        payConfig.setMchId(MCH_ID);
        //支付密钥 微信商户号后台设置的密钥
        payConfig.setMchKey(KEY);
        payConfig.setSubAppId(null);
        payConfig.setSubMchId(null);
        //支付证书 apiclient_cert.p12结尾
        payConfig.setKeyPath(path);//
        WxNewPayService wxPayService = new WxNewPayConfiguration().wxService(payConfig);
        return wxPayService;
    }
    /**
     * H5支付下单
     *
     * @param body        商品简介
     * @param orderNumber 订单号 全局唯一
     * @param money       支付金额 单位分
     * @param ip          用户下单的ip地址
     * @param notifyUrl   回调地址
     * @return 支付的时候 直接调用这个方法。
     * @throws Exception
     */
    public String wxH5Pay(String body, String orderNumber, int money, String ip, String notifyUrl) throws Exception {
        WxNewPayService wxNewPayService = getNewWxPayService();
        final WxPayUnifiedOrderRequest unifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
                .body(body)
                .outTradeNo(orderNumber)
                .totalFee(Math.toIntExact(money))
                .spbillCreateIp(ip)
                .notifyUrl(notifyUrl)
                .tradeType(WxPayConstants.TradeType.MWEB)
                .build();
        try {
            WxPayMwebOrderResult result = wxNewPayService.createOrder(unifiedOrderRequest);
            log.info("支付单号{},支付商户号{},支付appId{},支付金额{},回调地址{},支付结果{}", orderNumber, wxNewPayService.getConfig().getMchId(), wxNewPayService.getConfig().getAppId(), money, notifyUrl, result);
            return result.getMwebUrl();
        } catch (Exception e) {
            throw new RuntimeException("支付失败");
        }
    }

    /**
     * 加油微信支付回调
     *
     * @param wxPayOrderNotifyResult
     * @return
     * @throws Exception
     */
    @Override
    public void wxPayNotify(@NotNull WxPayOrderNotifyResult wxPayOrderNotifyResult, HttpServletResponse response) throws Exception {
        log.info("#########支付通知打印" + wxPayOrderNotifyResult + "#########");
        responseToPayed(response);
    }

    /**
     * 发起退款
     *
     * @param orderNumber
     * @param money
     * @param notifyUrl
     * @return
     */
    @Override
    public String refund(String orderNumber, Long money, String notifyUrl) {
        WxNewPayService wxNewPayService = getNewWxPayService();
        final WxPayRefundRequest refundRequest = WxPayRefundRequest.newBuilder()
                .outTradeNo(orderNumber)
                .totalFee(Math.toIntExact(money))
                .notifyUrl(notifyUrl)
                .outRefundNo(UUID.randomString(24))
                .refundFee(Math.toIntExact(money))
                .build();
        WxPayRefundResult refund = null;
        try {
            refund = wxNewPayService.refund(refundRequest);
            if (refund.getReturnCode().equals("FAIL")) {
                throw new RuntimeException("退款发起失败" + refund.getReturnMsg());
            }
        } catch (WxPayException e) {
            throw new RuntimeException("退款发起失败，原因：" + e.getErrCodeDes());
        }
        log.info("退款发起成功，退款订单号:" + refund.getOutTradeNo());
        return "退款发起成功，请耐心等待";
    }

    /**
     * 退款回调
     *
     * @param wxPayRefundNotifyResult
     * @param response
     * @return
     */
    @Override
    public String refundCallback(WxPayRefundNotifyResult wxPayRefundNotifyResult, HttpServletResponse response) throws IOException {
        WxPayRefundNotifyResult.ReqInfo reqInfo = wxPayRefundNotifyResult.getReqInfo();
        responseToPayed(response);
        log.info("#########退款结果打印" + reqInfo + "#########");
        if (reqInfo == null || StringUtils.isEmpty(reqInfo.getRefundStatus())) {
            log.info("#########退款结果为空#########");
            return "退款结果为空";
        }
        if (reqInfo.getRefundStatus().equals("SUCCESS")) {
            log.info("#########退款成功！（是否到账未知）");
            return "退款成功";
        } else if (reqInfo.getRefundStatus().equals("CHANGE")) {
            log.info("#########退款异常###########");
            return "退款异常";
        } else {
            log.info("#########退款关闭###########");
            return "退款关闭";
        }
    }

    /**
     * 支付成功回调响应返回success
     */
    public void responseToPayed(HttpServletResponse response) throws IOException {
        ServletOutputStream out = null;
        try {
            response.setContentType("application/json;charset=utf-8");
            out = response.getOutputStream();
            out.print("success");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null)
                out.close();
        }
    }

    /**
     * 验证微信支付通知的真实性
     *
     * @param notifyData 通知数据
     * @return 验证结果
     */
    @Override
    public boolean verifyNotify(String notifyData) {
        try {
            WxNewPayService wxPayService = getNewWxPayService();
            WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(notifyData);
            // 验证签名和结果
            return "SUCCESS".equals(notifyResult.getReturnCode())
                    && "SUCCESS".equals(notifyResult.getResultCode());
        } catch (Exception e) {
            log.error("微信支付通知验证失败", e);
            return false;
        }
    }

    @Override
    public Map<String, String> parseNotifyData(String xmlData) {
        try {
            return WXPayUtil.xmlToMap(xmlData);
        } catch (Exception e) {
            log.error("解析微信支付回调通知数据失败", e);
            return new HashMap<>();
        }
    }

    @Override
    public String responseSuccess() {
        Map<String, String> responseMap = new HashMap<>();
        responseMap.put("return_code", "SUCCESS");
        responseMap.put("return_msg", "OK");
        try {
            return WXPayUtil.mapToXml(responseMap);
        } catch (Exception e) {
            log.error("生成微信支付成功响应失败", e);
            return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
        }
    }

    @Override
    public String responseFailure(String message) {
        Map<String, String> responseMap = new HashMap<>();
        responseMap.put("return_code", "FAIL");
        responseMap.put("return_msg", message);
        try {
            return WXPayUtil.mapToXml(responseMap);
        } catch (Exception e) {
            log.error("生成微信支付失败响应失败", e);
            return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[" + message + "]]></return_msg></xml>";
        }
    }

    @Override
    public Map<String, String> wxMiniPay(String openId, String description, String orderNumber,
                                         int amount, String ip, String notifyUrl) {
        try {
            log.info("微信小程序支付开始，订单号：{}", orderNumber);
            PrepayRequest prepayRequest = new PrepayRequest();
            prepayRequest.setAppid(getNewWxPayService().getConfig().getAppId());
            prepayRequest.setMchid(getNewWxPayService().getConfig().getMchId());
            prepayRequest.setDescription(description);
            prepayRequest.setOutTradeNo(orderNumber);
            prepayRequest.setNotifyUrl(notifyUrl);
            Amount amount1 = new Amount();
            amount1.setTotal(amount);
            prepayRequest.setAmount(amount1);
            Payer payer = new Payer();
            payer.setOpenid(openId);
            prepayRequest.setPayer(payer);
            if (config == null) {
                config = new RSAAutoCertificateConfig.Builder()
                        .merchantId(MCH_ID)
                        .privateKey(privateKey)
                        .merchantSerialNumber(merchantSerialNumber)
                        .apiV3Key(KEY)
                        .build();
            }
            JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
            PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(prepayRequest);
            // 构建小程序调起支付的参数
            Map<String, String> payParams = new HashMap<>();
            payParams.put("appId", APP_ID);
            payParams.put("timeStamp", response.getTimeStamp());
            payParams.put("nonceStr", response.getNonceStr());
            payParams.put("package", response.getPackageVal());
            payParams.put("signType", "MD5");
            payParams.put("paySign", response.getPaySign());
            log.info("微信小程序支付参数生成成功，订单号：{}", orderNumber);
            return payParams;
        } catch (Exception e) {
            log.error("微信小程序支付异常", e);
            return null;
        }
    }

    @Override
    public String getOpenIdByCode(String code) {
        String accessTokenUrl = "https://api.weixin.qq.com/sns/jscode2session?appid="
                +APP_ID+"&secret="+appsecret+"&js_code="+code+"&grant_type=authorization_code";
        try {
            // 发送HTTP请求获取access_token
            String response = com.ruoyi.system.tools.HttpUtils.sendGet(accessTokenUrl, null);
            System.out.println(response);
            JSONObject data = JSONObject.parseObject(response);
            return data.getString("openid"); // 返回openid
        } catch (Exception e) {
            log.error("获取openid失败", e);
            return null; // 返回null表示获取失败
        }
    }

    public static void main(String[] args) throws WxPayException, GeneralSecurityException, IOException {

        com.alibaba.fastjson2.JSONObject jsonRe = com.alibaba.fastjson2.JSONObject.parseObject("{\n" +
                "\t\t\"original_type\": \"transaction\",\n" +
                "\t\t\"algorithm\": \"AEAD_AES_256_GCM\",\n" +
                "\t\t\"ciphertext\": \"Dc3eNCaKSNsnHD60dZwuSaPlGnGlmjVbto1S/0sT5HfCC/HxAavxk9olZd3lbgZ8DzyGkgzMsapkdwaEWAKHq5HlltSyTU7EK5ZHlrXu0bi0x4dGClg71+6J48Ee7KwWNjgYodje9yJ2CY3rGW8+xMhZA7Zu59Ym/gobpGAbJhu0KupMZde+5sVTCEOgI2LFstfgsbqLTDOMJz7OAOgZZV/QP794H1zvOGlesoPMNcgK5tT8IFby70/W24Cnfn0CGsP03F41zPi20GdmeLwKovAMa+d6NgvqcX4ZHCXzStqJkQCFXATzE5nkARlqD65wvk738za46ZgWPQnDl/AK23ItvyD6pdLQ1o1WjLDjYkRLNi1BKj75GHw7Lu8R4NqCYMd4NS4uIwkAYiOCq8DRO2ZpKViK1Dvq/I+O0MVwVdT0c+9TjRxFwgn9rbdDeMg9V64vbQWQ/clg7SDrFkdBoJgqtpN0iwP6UVk2jaS0du0ShqNWR2uCuwk1MZkzUwaVfsaM0EorDlwanQOU3iHQeEmszW+QEYdPJ93ave8AwAec+KWSAvv37fqDu2rANS6Yj5cntzFyQg==\",\n" +
                "\t\t\"associated_data\": \"transaction\",\n" +
                "\t\t\"nonce\": \"XCGY9Q8bCrXR\"\n" +
                "\t}");
        WxAesUtil wxAesUtil = new WxAesUtil("Qz4MAn5FwFFt112xJc82z99TwJTnrndT".getBytes());
        String re = wxAesUtil.decryptToString(jsonRe.getString("associated_data").getBytes(), jsonRe.getString("nonce").getBytes(), jsonRe.getString("ciphertext"));
        com.alibaba.fastjson2.JSONObject jsonObject = com.alibaba.fastjson2.JSONObject.parseObject(re);



        System.out.println(jsonObject);
    }
}
